package com.xxjqr.rbac.auth;

import com.alibaba.fastjson.JSON;
import com.xxjqr.rbac.enums.CodeMsgEnum;
import com.xxjqr.rbac.response.Response;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @描述 springsecurity 主配置文件
 * @码农 丁昌江
 * @日期 2021/3/4 17:15
 */
@Configuration
@AllArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final MyUserDetailsService userDetailsService;
    private final JwtTokenUtil jwtTokenUtil;
    private final DynamicSecurityMetadataSource dynamicSecurityMetadataSource;
    private final DynamicAccessDecisionManager dynamicAccessDecisionManager;

    @Override
    public void configure(WebSecurity web) throws Exception {
        //忽略的接口
        web.ignoring().antMatchers("/druid/**"
                , "/captcha/getCaptcha"
                , "/user/logout");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf()
                .disable()
                .sessionManagement()// 基于token，每次都通过JwtAuthenticationTokenFilter验证，所以无需把信息放在session中
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                //.requestMatchers(CorsUtils::isPreFlightRequest)
                //.permitAll()
                // 登录的接口不需要拦截
                //.antMatchers()
                //.permitAll()
                //// 跨域请求会先进行一次options请求
                //.antMatchers(HttpMethod.OPTIONS)
                //.permitAll()
                // 除上面外的所有请求全部需要鉴权认证
                .antMatchers("/user/login")
                .permitAll()
                //.antMatchers("/user/editUser").hasRole("super_admin")
                .anyRequest()
                .authenticated()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                        o.setSecurityMetadataSource(dynamicSecurityMetadataSource);
                        o.setAccessDecisionManager(dynamicAccessDecisionManager);
                        return o;
                    }
                });

        // 开启记住我的功能
        //http.rememberMe()
        //        .tokenRepository(persistentTokenRepository())
        //        .tokenValiditySeconds(120)
        //        .userDetailsService(userDetailsService);
        // 禁用缓存
        http.headers().cacheControl();
        // 在UsernamePasswordAuthenticationFilter执行前添加JWT filter器
        http.addFilterBefore(new JwtAuthenticationTokenFilter(userDetailsService, jwtTokenUtil), UsernamePasswordAuthenticationFilter.class);
        // 添加无权限或未登录时返回的自定义结果
        http.exceptionHandling()
                //登录了，但是没有权限
                .accessDeniedHandler((httpServletRequest, httpServletResponse, e) -> {
                    Response<Void> fail = Response.fail(CodeMsgEnum.AUTH_FAIL.getCode().toString(), CodeMsgEnum.AUTH_FAIL.getMsg());
                    httpServletResponse.setCharacterEncoding("UTF-8");
                    httpServletResponse.setContentType("application/json");
                    httpServletResponse.getWriter().println(JSON.toJSON(fail));
                    httpServletResponse.getWriter().flush();
                })
                //需要登录
                .authenticationEntryPoint((httpServletRequest, httpServletResponse, e) -> {
                    Response<Void> fail = Response.fail(CodeMsgEnum.LOGIN_FIRST.getCode().toString(), CodeMsgEnum.LOGIN_FIRST.getMsg());
                    httpServletResponse.setCharacterEncoding("UTF-8");
                    httpServletResponse.setContentType("application/json");
                    httpServletResponse.getWriter().println(JSON.toJSON(fail));
                    httpServletResponse.getWriter().flush();
                });
    }

    /*
     * 注入BCryptPasswordEncoder
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        //默认length长度是10
        return new BCryptPasswordEncoder();
    }


}
